﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Microsoft.Extensions.Logging;
using Volo.Abp.Application.Services;
using EcoSystem.Enums;
using Volo.Abp.MultiTenancy;

namespace EcoSystem
{
    public class TakeConfigAppService : CrudAppService<
         TakeConfig,
         TakeConfigDto,
         Guid,
         GetTakeConfigInput,
         CreateOrUpdateTakeConfigDto,
         CreateOrUpdateTakeConfigDto>, ITakeConfigAppService
    {
        private readonly IRepository<TakeNumber, Guid> _takeNumberRespository;
        private readonly Random random = new Random();
        public TakeConfigAppService(
       IRepository<TakeConfig, Guid> repository,
       IRepository<TakeNumber, Guid> takeNumberRespository
        )
       : base(repository)
        {
            _takeNumberRespository = takeNumberRespository;
        }

        public override async Task<TakeConfigDto> CreateAsync(CreateOrUpdateTakeConfigDto input)
        {
            if (!await Repository.AnyAsync(x => x.Code == input.Code && x.Status == true))
            {
                throw new UserFriendlyException("当前已有相同编码且已启用的取号配置、不允许重复添加");
            }
            return await base.CreateAsync(input);
        }
        public override async Task<TakeConfigDto> UpdateAsync(Guid id, CreateOrUpdateTakeConfigDto input)
        {
            if (!await Repository.AnyAsync(x => x.Code == input.Code && x.Status == true && x.Id != id))
            {
                throw new UserFriendlyException("当前已有相同编码且已启用的取号配置、不允许启用当前配置");
            }
            return await base.UpdateAsync(id, input);
        }


        public override async Task<PagedResultDto<TakeConfigDto>> GetListAsync(GetTakeConfigInput input)
        {
            var query = (await Repository.GetQueryableAsync());
            query = query
                 .WhereIf(!input.Code.IsNullOrWhiteSpace(), x => x.Code.StartsWith(input.Code))
                 .WhereIf(input.Status.HasValue, x => x.Status == input.Status);
            var total = await query.CountAsync();
            var items = await query.OrderBy(x => x.CreationTime).Skip(input.SkipCount).Take(input.MaxResultCount).ToListAsync();

            return new PagedResultDto<TakeConfigDto>
            {
                TotalCount = total,
                Items = await MapToGetListOutputDtosAsync(items)
            };
        }

        public override async Task DeleteAsync(Guid id)
        {
            await Repository.DeleteAsync(id);
        }

        public async Task<TakeNumberDto> GetNextTakeNumberAsync(GetNextTakeNumberInputDto input)
        {
            var query = (await _takeNumberRespository.GetQueryableAsync()).AsNoTracking();
            var item = await query.FirstOrDefaultAsync(x => x.Code == input.Code && x.Group == input.Group);
            TakeNumberDto result;
            var takeCount = input.TakeCount ?? 1;
            takeCount = takeCount < 1 ? 1 : takeCount;
            if (item == null)
            {
                result = new TakeNumberDto()
                {
                    Code = input.Code,
                    Group = input.Group,
                    CurrentNumber = input.StartNumber.HasValue ? input.StartNumber.Value + takeCount : takeCount
                };
            }
            else
            {
                item.CurrentNumber = item.CurrentNumber + takeCount;
                result = new TakeNumberDto()
                {
                    Code = item.Code,
                    Group = item.Group,
                    CurrentNumber = item.CurrentNumber,
                    CreationTime = item.CreationTime,
                    LastModificationTime = DateTime.Now
                };
            }
            if (input.IsUpdate)
            {
                if (item != null)
                {
                    await _takeNumberRespository.UpdateAsync(item, true);
                }
                else
                {
                    item = new TakeNumber()
                    {
                        Code = input.Code,
                        Group = input.Group,
                        CurrentNumber = result.CurrentNumber,
                        CreationTime = result.CreationTime,
                    };
                    EntityHelper.TrySetId(item, () => GuidGenerator.Create());
                    await _takeNumberRespository.InsertAsync(item, true);
                }
            }
            return result;
        }


        public async Task<string> CreateTakeNumberAsync(CreateTakeNumberInputDto input)
        {
            var config = await (await Repository.GetQueryableAsync()).AsNoTracking().Where(x => x.Code == input.Code && x.Status == true).FirstOrDefaultAsync();
            if (config == null)
            {
                throw new UserFriendlyException("取号失败，读取配置出错");
            }
            List<string> numberItems = new List<string>();
            foreach (var rule in config.RuleItems)
            {
                if (rule.Type == TakeRuleType.FixedValue)
                {
                    //固定值
                    numberItems.Add(GetFixedValueNumber(rule, input.Paratemers));
                }
                else if (rule.Type == TakeRuleType.RandNumber)
                {
                    //随机数
                    numberItems.Add(GetRandomNumber(rule));
                }
                else if (rule.Type == TakeRuleType.BusinessCode)
                {
                    //其它业务代码
                    numberItems.Add(GetBusinessNumber(rule, input.Paratemers));
                }
                else if (rule.Type == TakeRuleType.SerialNumber)
                {
                    //流水号
                    var values = numberItems.ToArray();
                    numberItems.Add(await GetSerialNumberAsync(config.Code, rule, values));
                }
                else
                {
                    //日期时间
                    numberItems.Add(GetDateTimeNumber(rule));
                }
            }
            return string.Join("", numberItems);
        }

        public async Task<List<string>> CreateTakeNumbersAsync(BatchCreateTakeNumberInputDto input)
        {
            if (input.Count <= 1)
            {
                throw new UserFriendlyException("参数错误，批量取号数量必须大于1");
            }
            var config = await (await Repository.GetQueryableAsync()).AsNoTracking().Where(x => x.Code == input.Code && x.Status == true).FirstOrDefaultAsync();
            if (config == null)
            {
                throw new UserFriendlyException("批量取号失败，读取配置出错");
            }
            if (!config.RuleItems.Any(x => x.Type == TakeRuleType.SerialNumber))
            {
                throw new UserFriendlyException("批量取号失败，未配置流水号的取号业务无法进行批量取号");
            }
            List<string> numberItems = new List<string>();
            TakeNumberDto takeNumber = null;
            TakeRuleItem serialRuleItem = null;
            foreach (var rule in config.RuleItems)
            {
                if (rule.Type == TakeRuleType.FixedValue)
                {
                    //固定值
                    numberItems.Add(GetFixedValueNumber(rule, input.Paratemers));
                }
                else if (rule.Type == TakeRuleType.RandNumber)
                {
                    //随机数
                    numberItems.Add(GetRandomNumber(rule));
                }
                else if (rule.Type == TakeRuleType.BusinessCode)
                {
                    //其它业务代码
                    numberItems.Add(GetBusinessNumber(rule, input.Paratemers));
                }
                else if (rule.Type == TakeRuleType.SerialNumber)
                {
                    //流水号
                    var values = numberItems.ToArray();
                    takeNumber = await GetSerialNumbersAsync(config.Code, rule, values, input.Count);
                    serialRuleItem = rule;
                    numberItems.Add("{serialNumber}");
                }
                else
                {
                    //日期时间
                    numberItems.Add(GetDateTimeNumber(rule));
                }
            }
            List<string> numbers = new List<string>();
            for (var i = 0; i < input.Count; i++)
            {
                //处理流水号
                var arrs = numberItems.Select(x =>
                {
                    if (x != "{serialNumber}") { return x; }
                    else
                    {
                        var len = serialRuleItem.Length ?? 6;
                        string text = ("000000000000" + takeNumber.CurrentNumber).Right(len);
                        takeNumber.CurrentNumber += 1;
                        return text;
                    }
                });
                numbers.Add(string.Join("", arrs));
            }
            return numbers;
        }



        /// <summary>
        /// 固定值取数
        /// </summary>
        /// <param name="rule"></param>
        /// <param name="paratemers"></param>
        /// <returns></returns>
        private string GetFixedValueNumber(TakeRuleItem rule, Dictionary<string, string> paratemers)
        {
            if (rule.AllowAfferent && !rule.Name.IsNullOrEmpty() && paratemers.ContainsKey(rule.Name) && !paratemers[rule.Name].IsNullOrEmpty())
            {
                return paratemers[rule.Name];
            }
            else
            {
                return rule.DefaultValue;
            }
        }

        /// <summary>
        /// 业务代码取值
        /// </summary>
        /// <param name="rule"></param>
        /// <param name="paratemers"></param>
        /// <returns></returns>
        private string GetBusinessNumber(TakeRuleItem rule, Dictionary<string, string> paratemers)
        {
            string value = paratemers.ContainsKey(rule.Name) ? paratemers[rule.Name] : rule.DefaultValue;
            if (rule.TakeLength.HasValue && rule.TakeLength.Value > 0)
            {
                //处理原始参数截取
                var index = rule.TakeIndex ?? 0;
                var len = rule.TakeLength.Value;
                var method = rule.TakingMethod ?? TakingMethod.LeftValue;
                if (method == TakingMethod.LeftValue)
                {
                    value = value.Substring(index, len);
                }
                else
                {
                    value = value.Substring(value.Length - index - len - 1, len);
                }
            }
            if (rule.Length.HasValue && rule.Length > 0 && rule.Length.Value > value.Length)
            {
                //处理补位
                var len = rule.Length.Value;
                var method = rule.FillingMethod ?? FillingMethod.LeftFilling;
                string fillingChar = rule.FillingChar.IsNullOrEmpty() ? "0" : rule.FillingChar.Substring(0, 1);
                string fillString = CreateFillString(fillingChar, rule.Length.Value);
                if (method == FillingMethod.LeftFilling)
                {
                    string text = fillString + value;
                    return text.Right(len);
                }
                else
                {
                    string text = value + fillString;
                    return text.Left(len);
                }
            }
            return value;
        }

        /// <summary>
        /// 随机数取数
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        private string GetRandomNumber(TakeRuleItem item)
        {
            var len = item.Length ?? 4;
            var text = "1234567890";
            if (item.RandomType == RandomValueType.NumberAndLetter)
            {
                text = "ABCDEFGHJKLMNPQRSTUVWXYZ1234567890";
            }
            else if (item.RandomType == RandomValueType.Letter)
            {
                text = "ABCDEFGHJKLMNPQRSTUVWXYZ";
            }
            string result = "";
            for (int i = 0; i < len; i++)
            {
                result += text.Substring(random.Next(0, text.Length), 1);
            }
            return result;
        }

        /// <summary>
        /// 日期时间取数
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        private string GetDateTimeNumber(TakeRuleItem item)
        {
            switch (item.Type)
            {
                case TakeRuleType.DateYYYYMMDD:
                    return DateTime.Today.ToString("yyyyMMdd");
                case TakeRuleType.DateYYYY:
                    return DateTime.Today.ToString("yyyy");
                case TakeRuleType.DateYY:
                    return DateTime.Today.ToString("yy");
                case TakeRuleType.DateMM:
                    return DateTime.Today.ToString("MM");
                case TakeRuleType.DateDD:
                    return DateTime.Today.ToString("DD");
                case TakeRuleType.TimeHHmmss:
                    return DateTime.Now.ToString("HHmmss");
                case TakeRuleType.TimeHHmm:
                    return DateTime.Now.ToString("HHmm");
                case TakeRuleType.Timefff:
                    return DateTime.Now.ToString("fff");
                case TakeRuleType.TimeStamp12:
                    return DateTime.Now.ToString("HHmm");
                case TakeRuleType.TimeStamp13:
                    return DateTime.Now.ToString("HHmm");
                default:
                    return "";
            }
        }

        /// <summary>
        /// 流水号取值
        /// </summary>
        /// <param name="code"></param>
        /// <param name="item"></param>
        /// <param name="values"></param> 
        /// <returns></returns>
        private async Task<string> GetSerialNumberAsync(string code, TakeRuleItem item, string[] values)
        {
            string groupKey = "";
            item.GroupRuleIndexes.ForEach(index =>
            {
                groupKey = values[index];
            });
            var serialNumber = await GetNextTakeNumberAsync(new GetNextTakeNumberInputDto()
            {
                Code = code,
                Group = groupKey,
                IsUpdate = true,
                StartNumber = item.StartNumber
            });
            var len = item.Length ?? 6;
            return ("000000000000" + serialNumber.CurrentNumber).Right(len);
        }

        /// <summary>
        /// 批量更新流水取号
        /// </summary>
        /// <param name="code"></param>
        /// <param name="item"></param>
        /// <param name="values"></param>
        /// <param name="takeCount"></param>
        /// <returns></returns>
        private async Task<TakeNumberDto> GetSerialNumbersAsync(string code, TakeRuleItem item, string[] values, int takeCount)
        {
            string groupKey = "";
            item.GroupRuleIndexes.ForEach(index =>
            {
                groupKey = values[index];
            });
            return await GetNextTakeNumberAsync(new GetNextTakeNumberInputDto()
            {
                Code = code,
                Group = groupKey,
                IsUpdate = true,
                StartNumber = item.StartNumber,
                TakeCount = takeCount
            });
        }

        /// <summary>
        /// 生成指定长度的填充字符串
        /// </summary>
        /// <param name="fillChar"></param>
        /// <param name="len"></param>
        /// <returns></returns>
        private string CreateFillString(string fillChar, int len)
        {
            string value = "";
            for (int i = 0; i < len; i++)
            {
                value += fillChar;
            }
            return value;
        }

    }
}
